package com.example.service;

import com.example.dto.RoleDTO;
import com.example.entity.RoleDO;
import com.example.exception.BusinessException;
import com.example.mapper.RoleMapper;
import com.example.service.impl.RoleServiceImpl;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

class RoleServiceTest {

    @Mock
    private RoleMapper roleMapper;

    @InjectMocks
    private RoleServiceImpl roleService;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void testAddRole_Success() {
        RoleDTO roleDTO = new RoleDTO();
        roleDTO.setRoleName("testRole");
        roleDTO.setDescription("testDescription");

        when(roleMapper.selectOne(any())).thenReturn(null);
        when(roleMapper.insert(any())).thenReturn(1);

        assertTrue(roleService.addRole(roleDTO));

        verify(roleMapper).selectOne(any());
        verify(roleMapper).insert(any());
    }

    @Test
    void testAddRole_Failure() {
        RoleDTO roleDTO = new RoleDTO();
        roleDTO.setRoleName("testRole");
        roleDTO.setDescription("testDescription");

        RoleDO existingRole = new RoleDO();
        existingRole.setRoleId(1);
        existingRole.setRoleName("testRole");
        existingRole.setCreateTime(new Date());
        existingRole.setUpdateTime(new Date());

        when(roleMapper.selectOne(any())).thenReturn(existingRole);

        assertThrows(BusinessException.class, () -> roleService.addRole(roleDTO));

        verify(roleMapper).selectOne(any());
        verify(roleMapper, never()).insert(any());
    }

    @Test
    void testDeleteRole_Success() {
        Integer roleId = 1;
        RoleDO roleDO = new RoleDO();
        roleDO.setRoleId(roleId);
        roleDO.setRoleName("testRole");
        roleDO.setCreateTime(new Date());
        roleDO.setUpdateTime(new Date());

        when(roleMapper.selectById(roleId)).thenReturn(roleDO);
        when(roleMapper.deleteById(roleId)).thenReturn(1);

        assertTrue(roleService.deleteRole(roleId));

        verify(roleMapper).selectById(roleId);
        verify(roleMapper).deleteById(roleId);
    }

    @Test
    void testDeleteRole_Failure() {
        Integer roleId = 1;

        when(roleMapper.selectById(roleId)).thenReturn(null);

        assertThrows(BusinessException.class, () -> roleService.deleteRole(roleId));

        verify(roleMapper).selectById(roleId);
        verify(roleMapper, never()).deleteById(roleId);
    }

    @Test
    void testEditRole_Success() {
        RoleDTO roleDTO = new RoleDTO();
        roleDTO.setRoleId(1);
        roleDTO.setRoleName("newRoleName");
        roleDTO.setDescription("newDescription");

        RoleDO existingRole = new RoleDO();
        existingRole.setRoleId(1);
        existingRole.setRoleName("oldRoleName");
        existingRole.setCreateTime(new Date());
        existingRole.setUpdateTime(new Date());

        when(roleMapper.selectById(roleDTO.getRoleId())).thenReturn(existingRole);
        when(roleMapper.selectOne(any())).thenReturn(null);
        when(roleMapper.updateById(any())).thenReturn(1);

        assertTrue(roleService.editRole(roleDTO));

        verify(roleMapper).selectById(roleDTO.getRoleId());
        verify(roleMapper).selectOne(any());
        verify(roleMapper).updateById(any());
    }

    @Test
    void testEditRole_Failure_NotFound() {
        RoleDTO roleDTO = new RoleDTO();
        roleDTO.setRoleId(1);
        roleDTO.setRoleName("newRoleName");
        roleDTO.setDescription("newDescription");

        when(roleMapper.selectById(roleDTO.getRoleId())).thenReturn(null);

        assertThrows(BusinessException.class, () -> roleService.editRole(roleDTO));

        verify(roleMapper).selectById(roleDTO.getRoleId());
        verify(roleMapper, never()).selectOne(any());
        verify(roleMapper, never()).updateById(any());
    }

    @Test
    void testEditRole_Failure_DuplicateName() {
        RoleDTO roleDTO = new RoleDTO();
        roleDTO.setRoleId(1);
        roleDTO.setRoleName("newRoleName");
        roleDTO.setDescription("newDescription");

        RoleDO existingRole = new RoleDO();
        existingRole.setRoleId(1);
        existingRole.setRoleName("oldRoleName");
        existingRole.setCreateTime(new Date());
        existingRole.setUpdateTime(new Date());

        RoleDO duplicateRole = new RoleDO();
        duplicateRole.setRoleId(2);
        duplicateRole.setRoleName("newRoleName");
        duplicateRole.setCreateTime(new Date());
        duplicateRole.setUpdateTime(new Date());

        when(roleMapper.selectById(roleDTO.getRoleId())).thenReturn(existingRole);
        when(roleMapper.selectOne(any())).thenReturn(duplicateRole);

        assertThrows(BusinessException.class, () -> roleService.editRole(roleDTO));

        verify(roleMapper).selectById(roleDTO.getRoleId());
        verify(roleMapper).selectOne(any());
        verify(roleMapper, never()).updateById(any());
    }

    @Test
    void testQueryRoles() {
        String roleName = "testRole";
        List<RoleDO> roles = new ArrayList<>();
        RoleDO roleDO = new RoleDO();
        roleDO.setRoleId(1);
        roleDO.setRoleName(roleName);
        roleDO.setCreateTime(new Date());
        roleDO.setUpdateTime(new Date());
        roles.add(roleDO);

        when(roleMapper.selectList(any())).thenReturn(roles);

        List<RoleDO> queriedRoles = roleService.queryRoles(roleName);

        assertEquals(roles.size(), queriedRoles.size());
        assertEquals(roleName, queriedRoles.get(0).getRoleName());

        verify(roleMapper).selectList(any());
    }
}